package by.bsuir.csschat.manager;


import by.bsuir.csschat.channel.ChanAdvertiserThread;
import by.bsuir.csschat.channel.Channel;
import by.bsuir.csschat.privconv.PrivateConversation;
import by.bsuir.csschat.user.User;
import by.bsuir.csschat.messages.PrivateMessage;
import by.bsuir.csschat.dispatchers.MulticastDispatcher;
import by.bsuir.csschat.dispatchers.NetworkDispatcher;
import by.bsuir.csschat.messages.AttachmentMessage;
import by.bsuir.csschat.messages.ChannelMessage;
import by.bsuir.csschat.messages.ServiceMessage;
import by.bsuir.csschat.messages.Message;
import by.bsuir.csschat.ui.*;



import java.awt.Frame;
import java.util.*;

import javax.swing.*;

public class Manager {
    
    private static Manager singObj;
    
    
    public static Manager GetInstance() {//шаблон singlenton
        try {
            if(singObj==null)
                singObj=new Manager();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return singObj;
    }
  
    
    public static final int DefaultOperTimeout=3000;
    private static final int MAX_RECVFILES_NUM=50;
    
    private NetworkDispatcher _Dispatcher;
    private ArrayList _ChannelList=new ArrayList();//список каналов
    private TreeMap _privconvs=new TreeMap();//коллекция приватных подключений
    private Channel _curChan;//текущий канал
    private User _me;//я
    private ChanAdvertiserThread _advThread;
    private boolean _ChannelFree=true;//свободен ли канал
    private LinkedList _recvfiles=new LinkedList();//отправленные файлы
    private frmRecvFiles _recvui;//форма отображения отправленных файлов
    private String _ReqChan="";

    
    public Object WaitForJoinAck=new Object();
    
    public NetworkDispatcher GetDispatcher(){
        return _Dispatcher;
    }
    
    private Manager() throws Exception{
        _Dispatcher=new MulticastDispatcher();
        _me=User.Anonymous; //анонимный пользователь
    }

    public void ShowRecvFiles(){
       AttachmentMessage []recvArr=new AttachmentMessage[_recvfiles.size()];
        
        try {
            if(_recvui!=null)
                _recvui.dispose();
            _recvfiles.toArray(recvArr);
            _recvui=new frmRecvFiles(recvArr);
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public void SendFileToAll(){
        try {
            
            java.io.File chFile=FileDialog.OpenFileDialog();
            if(chFile==null)
                return;
            if(!chFile.exists())
                return;
            if(chFile.length()>GetDispatcher().GetMaxFileSize()){
                JOptionPane.showMessageDialog(null,"File is too big (max size: "+ GetDispatcher().GetMaxFileSize() +" bytes)","Error",JOptionPane.ERROR_MESSAGE);
                return;
            }
            AttachmentMessage newMsg=new AttachmentMessage(GetMe(),chFile,false);
            GetDispatcher().DispatchToAll(newMsg);
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(null,ex.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
        }
    }
    
    public boolean TrySetNick(String iNick){//попытка получить ник
        _me=new User(iNick);
        _me.SetStatus(User.STATUS_ASKINGNICK);
        ServiceMessage newMsg=new ServiceMessage(User.Anonymous,ServiceMessage.CODE_QUERY_NICK_FREE,iNick);        
        GetDispatcher().DispatchToAll(newMsg);
        try {
            Thread.sleep(DefaultOperTimeout);
        } catch (InterruptedException ex) {}
        if(_me.GetStatus()==User.STATUS_NICKFAILED)
            return false;
        
        _me.SetStatus(User.STATUS_AUTH);
        return true;
    }
    
    public void Quit(){
        if(_curChan!=null){
            ServiceMessage newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_PART,_curChan.GetName());
            GetDispatcher().DispatchToAll(newMsg);
        }
        System.exit(0);
    }
    
    public boolean IsChannelFree(String iChanName){//определение свободен ли канал
        _ChannelFree=true;
        ServiceMessage newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_QUERY_CHAN_FREE,iChanName);  
        GetDispatcher().DispatchToAll(newMsg);
        try {
            Thread.sleep(DefaultOperTimeout);
        } catch (InterruptedException ex) {}
        return _ChannelFree;
    }
    
    
    public String[] GetAvailableChannels(){//получить список доступных каналов
        String []outArr=new String[_ChannelList.size()];
        _ChannelList.toArray(outArr);
        return outArr;
    }
    
    public void SetAndAdvertiseChannel(Channel iChan){//создать канал и запустить
        _curChan=iChan;
        _curChan.AddUser(GetMe());
        _ReqChan="";
        if(_advThread!=null)
            _advThread.stop();
        _advThread=new ChanAdvertiserThread(_curChan);
        _advThread.start();
    }
    
    public void SendChanMessage(String iMsg){//отправить сообщение в канал
        if(_curChan==null)
            return;

        ChannelMessage newMsg=new ChannelMessage(iMsg,GetMe(),_curChan);
        GetDispatcher().DispatchToAll(newMsg);

    }
    
    public Channel GetCurrentChannel(){//получить текущий поток
        return _curChan;
    }
    
    public boolean HasJoined(){//есть ли подключение
        return _curChan!=null;
    }
    
    public void SendPrivateMsg(User iTo,String iMsg){//отправить приватное сообщение
        PrivateMessage newMsg=new PrivateMessage(iMsg,GetMe(),iTo);
        GetDispatcher().DispatchToAll(newMsg);
    }
    
    public void StartPrivateConversation(User iTo){//начать приватное соединение
        if(iTo==null)
            return;
        if(!_privconvs.containsKey(iTo)){
            _privconvs.put(iTo,new PrivateConversation(iTo));
        }else
        	((PrivateConversation)_privconvs.get(iTo)).Show();
    }
    
 
   
    
    public void ParseMessage(Message iMsg){//обработка сообщения
        if(iMsg instanceof ServiceMessage)
            ParseServiceMessage((ServiceMessage)iMsg);
        else if(iMsg instanceof ChannelMessage)
            ParseChannelMessage((ChannelMessage)iMsg);
        else if(iMsg instanceof PrivateMessage)
            ParsePrivateMessage((PrivateMessage)iMsg);
        else if(iMsg instanceof AttachmentMessage)
            ParseAttachmentMessage((AttachmentMessage)iMsg);
       
    }

   
    
    private void ParseAttachmentMessage(AttachmentMessage iMsg){//обработка входящего файла
        if(!iMsg.IsRequested()){
            _recvfiles.add(iMsg);
            if(_recvfiles.size()>MAX_RECVFILES_NUM)
                _recvfiles.removeFirst();
            if(_curChan!=null)
                _curChan.Notice("New file received " + iMsg);
        }
    }
    
    private void ParsePrivateMessage(PrivateMessage iMsg){//обработка входящего приватного сообщения
        if(!iMsg.GetTo().equals(GetMe()))
            return;
        
        StartPrivateConversation(iMsg.GetSender());
        ((PrivateConversation)_privconvs.get(iMsg.GetSender())).MessageArrival(iMsg.GetText());
    }
    
    private void ParseChannelMessage(ChannelMessage iMsg){//обратботка канального сообщения
        if(iMsg.GetChannel()==null)
            return;
        if(!iMsg.GetChannel().equals(_curChan))
            return;

        iMsg.GetChannel().MessageReceived(iMsg);
    }
    
    private void ParseServiceMessage(ServiceMessage iMsg){//обработка сервисного сообщения
        if(!iMsg.IsBroadcast() && !iMsg.GetToUser().equals(GetMe()))
            return;
        
        switch(iMsg.GetCode()){
            case ServiceMessage.CODE_CHAN_ADV:{
                if(!_ChannelList.contains(iMsg.GetArg())){
                    _ChannelList.add(iMsg.GetArg());
                    System.out.println("New channel discovered " +iMsg.GetArg());
                }
            }
                break;
            case ServiceMessage.CODE_JOIN:{//подключение к каналу
                if(_curChan==null){
                    _ReqChan=iMsg.GetArg();
                }else{
                    if(!iMsg.GetArg().equals(_curChan.GetName()))
                        return;
                    _curChan.Join(iMsg.GetSender());

                    //сообщение о подключении пользователя к каналу
                    ServiceMessage newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_HELOJOIN,_curChan.GetName());
                    GetDispatcher().DispatchToAll(newMsg);
                    
                    //если я владелец канала, сообщить о подключении
                    if(GetMe().equals(_curChan.GetOwner())){
                        newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_CHAN_OWNER,_curChan.GetName());
                        GetDispatcher().DispatchToAll(newMsg);
                    }
                    
                }
            } break;
            
            case ServiceMessage.CODE_HELOJOIN:{//удачное подключение
                if(_curChan==null && _ReqChan.length()>0 && _ReqChan.equals(iMsg.GetArg())){
                    System.out.println("Join Accepted");
                    SetAndAdvertiseChannel(new Channel(iMsg.GetArg()));
                    _curChan.AddUser(GetMe());
                    synchronized(WaitForJoinAck){ WaitForJoinAck.notify();}
                }else if(_curChan==null && _ReqChan.length()<=0)        
                    return;
                
                if(! iMsg.GetArg().equals(_curChan.GetName()))
                    return;
                
                
                _curChan.AddUser(iMsg.GetSender());                
            }break;

            case ServiceMessage.CODE_PART:{//выход
                if(_curChan==null) return;
                if(!iMsg.GetArg().equals(_curChan.GetName()))
                    return;
                _curChan.Part(iMsg.GetSender());

            }break;
            
            case ServiceMessage.CODE_CHAN_OWNER:{//владелец канала
                if(_curChan==null) return;
                if(!iMsg.GetArg().equals(_curChan.GetName()))
                    return;
                _curChan.SetOwner(iMsg.GetSender());
            }break;
            
            case ServiceMessage.CODE_QUERY_NICK_FREE:{
                if(GetMe().GetName().equals(iMsg.GetArg()) && GetMe().GetStatus()==User.STATUS_AUTH){
                    ServiceMessage newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_NICK_TAKEN,GetMe().GetName());
                    GetDispatcher().DispatchToAll(newMsg);
                }
            }break;
            
            case ServiceMessage.CODE_QUERY_CHAN_FREE:{//свободный канал
                if(Channel.GetByName(iMsg.GetArg())!=null){
                    ServiceMessage newMsg=new ServiceMessage(GetMe(),ServiceMessage.CODE_CHAN_TAKEN,iMsg.GetArg());
                    GetDispatcher().DispatchToAll(newMsg);
                }
            }break;
            
            case ServiceMessage.CODE_CHAN_TAKEN:{//занят ли канал
                if(_curChan.GetName().equals(iMsg.GetArg()))
                    _ChannelFree=false;
            }break;
            
            case ServiceMessage.CODE_NICK_TAKEN:{//занят ли ник
                if(GetMe().GetStatus()!=User.STATUS_ASKINGNICK) return;
                GetMe().SetStatus(User.STATUS_NICKFAILED);
            }break;
           

            
        }
    }
    
    public User GetMe(){
        return _me;
    }
    
}
